classdef C_OpticalChannel < handle
    %C_CHANNEL Summary of this class goes here
    %   Detailed explanation goes here
    
    properties (GetAccess = {?C_Eval, ?C_Rx}, SetAccess='private')
        Param;
        TxObj;
        
        ChIR;
    end
    
    properties (GetAccess='private', SetAccess='private')   
%         ChIR;
        SigPwr;
        EsPerNo;
    end

    methods (Access = private)
        function this = C_OpticalChannel(TxObj)
            this.TxObj = TxObj;
        end
    end
    
    methods (Static)
        function this = Instance(TxObj)
             persistent this_obj;
             
             if isempty(this_obj) || ~isvalid(this_obj)
                this_obj = C_OpticalChannel(TxObj);
             end
             
             this = this_obj;            
        end          
        
        function Initialise(this, param)
            this.Param = param;      
            
            IR = open('IR_sim1.mat');
            
            this.ChIR = IR.IR_sim1;
        end
        
        function SigOut = AddChannelResponse(this, SigIn)
            SigOut = SigIn;
            measSigPwr(this, SigIn);
            SigOut = addChannelResponse(this, SigOut);
            SigOut = addWhiteNoise(this, SigOut);
        end
    end
end

function SigOut = addWhiteNoise(this, SigIn)
    SigIn = reshape(SigIn, 1, []);
    this.EsPerNo = 10.^(this.Param.EbPerNo/10)...
         * ( 1 / (1 + this.TxObj.Param.GI_sizePct/100) )...
         * ( (length(this.TxObj.Param.ActiveModBand) + length(this.TxObj.Param.PilotModBand)) / (this.TxObj.Param.numSubcarriers))...
         * log2(this.TxObj.M);   
     
    NoisePwr = this.SigPwr ./ this.EsPerNo;  
    RealNoise = randn(1, length(SigIn));
    ImagNoise = randn(1, length(SigIn));
    Noise = complex(RealNoise, ImagNoise);
    SigOut = reshape((repmat(SigIn, length(NoisePwr), 1) + sqrt(NoisePwr/2)' * Noise).', this.TxObj.sigSize(1), this.TxObj.sigSize(2), []);
end

function measSigPwr(this, SigIn)
    SigIn = reshape(SigIn, 1, []);
    this.SigPwr = sum(SigIn.^2)/length(SigIn);
end

function SigOut = addChannelResponse(this, SigIn)
%     SigOut = filter(this.ChIR, 1, SigIn);
    SigOut = real(ifft(repmat(fft(this.ChIR.', size(SigIn,1)), [1, size(SigIn,2)]) .* fft(SigIn)));   
end

